#include "Psql.h"
#include "pg_type.h"
#include <SingletonHolder.h>
#include <Tools.h>
#include <stdexcept>



namespace DB{

// ----------------------------------------------------------------------------------------------------------- TransactionInsert

TransactionInsert::TransactionInsert(std::string theTableName, DB::Datapair &theData) : pqxx::transactor<>("Insert")


{
    myDatapair = theData;
	this->myTableName = theTableName;
}

TransactionInsert::TransactionInsert(std::string &theSQLinsert) : pqxx::transactor<>("Insert"),
																  mySQLstm(theSQLinsert)
{}

void TransactionInsert::operator ()(argument_type &T)
{
	if(!mySQLstm.empty())
	{
		T.exec(mySQLstm.c_str());
	}
	else
	{
		//vytvorime SQL vetu na Insert
		std::string _sql_insert = "INSERT INTO "+myTableName+" (";

		//pridame do vety nazvy stlpcov
		for(std::size_t i=0; i < myDatapair.size()-1;i++)
		{
			_sql_insert += myDatapair[i].first +",";
		}
		//pridame posledny nazov stpca ukonceni ) namiesto ,
		_sql_insert += myDatapair[myDatapair.size()-1].first + ") VALUES(";

		//postupne pridame data
		//
		// TODO!
		//	Zmenit pridavanie dat na daco viac generickejsie,efektivnejsie, lepsie
		//  teraz pouzivana metoda suxs hard!
		//
		for(std::size_t i=0; i < myDatapair.size()-1; i++)
		{
			ConversionWrapper aWrapper = convert_any(myDatapair[i].second);
			format_data(_sql_insert,aWrapper,false);
		}

        ConversionWrapper aWrapper = convert_any(myDatapair[myDatapair.size()-1].second);
		format_data(_sql_insert,aWrapper,true);

		//ak mame vygenerovany retazec ta spravime insert

		T.exec(_sql_insert.c_str());
	}
}

void TransactionInsert::on_comit()
{
	SingletonHolder::Instance().WriteMsg("Data inserted into table: "+myTableName+" successfuly.");
}

// ----------------------------------------------------------------------------------------------------------- end TransactionInsert

// ----------------------------------------------------------------------------------------------------------- TransactionSelect

TransactionSelect::TransactionSelect(std::string &theSQLselect,std::vector<Datapair> &theResult) : pqxx::transactor<>("Select"),
																								   mySQLselect(theSQLselect),
																								   myResult(theResult)
{}

void TransactionSelect::operator ()(argument_type &T)
{
	mySQLresult = T.exec(mySQLselect.c_str());

	if(mySQLresult.empty())
		return;

	//ziskame nazvy stlpcov ktore boli afektovane SQL query

	for(pqxx::result::const_iterator iter = mySQLresult.begin(); iter != mySQLresult.end(); iter++)
	{
		Datapair _row;
		for(std::size_t i=0; i < mySQLresult.columns(); i++)
		{
			pqxx::oid _type = iter[i].type();
			if(_type == BOOLOID)
			{
				bool _value;
				iter[i].to(_value);
				_row.push_back(std::make_pair<std::string,boost::any>(mySQLresult.column_name(i),boost::any(_value)));
			}
			else if( (_type >= INT8OID && _type <= INT4OID) || (_type == NUMERICOID) )
			{
				int _value;
				iter[i].to(_value);
				_row.push_back(std::make_pair<std::string,boost::any>(mySQLresult.column_name(i),boost::any(_value)));
			}
			else if( _type == VARCHAROID || _type == TEXTOID || _type == DATEOID)
			{
				std::string _value;
				iter[i].to(_value);
				_row.push_back(std::make_pair<std::string,boost::any>(mySQLresult.column_name(i),boost::any(_value)));
			}
			else if(_type == FLOAT4OID || _type == FLOAT8OID)
			{
				float _value;
				iter[i].to(_value);
				_row.push_back(std::make_pair<std::string,boost::any>(mySQLresult.column_name(i),boost::any(_value)));
			}
		}//end of iner for

		myResult.push_back(_row);
	}
}//end of operator()

void TransactionSelect::on_comit()
{
	if(myResult.size() > 0)
	{
		SingletonHolder::Instance().WriteMsg("Data query successfull & data retrieved");
	}
	else
	{
		SingletonHolder::Instance().WriteMsg("Data query sucessfull however no data were retrieved!");
	}
}

// ----------------------------------------------------------------------------------------------------------- end TransactionSelect

// ----------------------------------------------------------------------------------------------------------- Psql

Psql::Psql(std::string theConString) : myAttemp_count(3)
{
	myConnection = new pqxx::connection(theConString);
	//int myAttemp_count = 3;
}

Psql::~Psql(void)
{
	myConnection->disconnect();
	delete myConnection;
}

std::string Psql::CreateConnectionString(std::string theAdress,
										 std::string thePort,
										 std::string theDB,
										 std::string theUsrname,
										 std::string thePassw)
{
	//Ukazka spravneho connection string
	//
	//"hostaddr=127.0.0.1 port=3333 dbname=maindb user=plagadmin password=Plagadmin1234"
	//
	std::string constring = "hostaddr=[IP] port=[PORT] dbname=[DBNAME] user=[USRNAME] password=[PASSW]";

	//////////////////////////////////////////////////////////////////////////
	// ! TODO	! Popripade este doplnit nahradu prazdnych retazcov na		//
	//			! default hodnoty :)										//
	//////////////////////////////////////////////////////////////////////////

	Tools::Replace<std::string>(constring,"[IP]",theAdress);
	Tools::Replace<std::string>(constring,"[PORT]",thePort);
	Tools::Replace<std::string>(constring,"[DBNAME]",theDB);
	Tools::Replace<std::string>(constring,"[USRNAME]",theUsrname);
	Tools::Replace<std::string>(constring,"[PASSW]",thePassw);

	return constring;
}


bool Psql::Insert(std::string theTableName, std::vector<std::pair<std::string,boost::any> > &theData)
{
	try
	{
		myConnection->perform(TransactionInsert(theTableName,theData),myAttemp_count);
		return true;
	}
	catch(...)
	{
		SingletonHolder::Instance().WriteEx("All attempts to insert data to the database have failed!");
		return false;
	}
}

bool Psql::Insert(std::string &theSQLstr)
{
	try
	{
		myConnection->perform(TransactionInsert(theSQLstr),myAttemp_count);
		return true;
	}
	catch (...)
	{
		SingletonHolder::Instance().WriteEx("All attempts to insert data to the database have failed!");
		return false;
	}
}

bool Psql::Select(std::string &theSQLquery, std::vector<Datapair> &theData)
{
	try
	{
		myConnection->perform(TransactionSelect(theSQLquery,theData),myAttemp_count);

		return true;
	}
	catch (...)
	{
		SingletonHolder::Instance().WriteEx("All attempts to select data from the database have failed!");
		return false;
	}
}


bool Psql::Delete(std::string &theSQL)
{
	throw std::runtime_error("Psql::Delete method not implemented!");
}

bool Psql::Update(std::string &theSQL)
{
	throw std::runtime_error("Psql::Update method not implemented!");
}


bool Psql::Update(std::string theTableName,std::vector<std::pair<std::string,boost::any> > &newData ,std::vector<std::pair<std::string,boost::any> > &oldData)
{
	throw std::runtime_error("Psql::Update method not implemented!");
}

// ----------------------------------------------------------------------------------------------------------- end of Psql

}//end of DB
